﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Whitepages
{
    public class API
    {
        public enum QueryTypes { FindPerson, ReversePhone, ReverseAddress, FindBusiness }        
        static API()
        {
            URLList = new Dictionary<QueryTypes, string>();
            URLList.Add(QueryTypes.FindBusiness, @"http://api.whitepages.com/find_business/1.0/?{0};api_key={1}");
            URLList.Add(QueryTypes.FindPerson, @"http://api.whitepages.com/find_person/1.0/?{0};api_key={1}");
            URLList.Add(QueryTypes.ReverseAddress, @"http://api.whitepages.com/reverse_address/1.0/?{0};api_key={1}");
            URLList.Add(QueryTypes.ReversePhone, @"http://api.whitepages.com/reverse_phone/1.0/?phone={0};api_key={1}");
        }
        public static Dictionary<QueryTypes, string> URLList;
        public static string APIKey;


        /// <summary>
        ///Finds contact information for a person, based on name and location.
        ///Remarks
        ///Note that only one of the parameters lastname or name is required. However, we recommend providing as much information as you have as that will give better results and faster response.
        ///
        ///The name parameter is an optional way to specify the name of the person to be searched. For applications which use a single field name, this parameter can be passed directly to the WhitePages.com API. For applications which use separate firstname and lastname fields, these can be passed directly to the WhitePages.com API in their respective fields.
        ///
        ///Note that the usual address line spans two parameters. The house number is separated from the street name. For a typical address such as 2468 East Main St, this would make the house parameter 2468 and the street parameter "East Main St". Of course, spaces in the street name should be encoded as %20 for passing over HTTP, so the street parameter as passed to the API would be "East%20Main%20St"
        ///
        ///The firstname and lastname parameters support both an exact name match as well as a "begins with" match. To search for "Bernie" or "Bert" set the firstname parameter to "Ber*". The asterisk ("*") is called a "wild card" character. No other parameters support this character and it can only be used to match the end of a first name or the end of a last name.
        ///
        ///Searches for common nicknames, such as searching for "Doug" or "Douglas" as a first name, are automatically done without requiring the wildcard in the firstname field. If multiple matches are found the listings will be ordered so that more exact matches to the search parameters will be presented first with nicknames or other wildcard results lower in the results set.
        ///
        ///The metro parameter is a Boolean value where 1 signifies true or 0 signifies false. It specifies whether to expand the search to include the metropolitan area around the specified city parameter value.
        ///
        ///Limitations
        ///A find_person search will return at most 20 listings using the WPAPI. Additional listings may be available by following one of the URLs returned as links with a successful search.
        ///
        /// </summary>
        /// <param name="Firstname"></param>
        /// <param name="Lastname"></param>
        /// <param name="Name"></param>
        /// <param name="House"></param>
        /// <param name="Street"></param>
        /// <param name="City"></param>
        /// <param name="State"></param>
        /// <param name="Zip"></param>
        /// <param name="AreaCode"></param>
        /// <param name="Metro"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        public static bool FindPerson(string Firstname, string Lastname, string Name, string House, string Street, string City, string State, string Zip, string AreaCode, bool Metro, out Whitepages.wp Result)
        {
            System.Text.StringBuilder sb = new StringBuilder();
            if (Firstname != null && Firstname.Trim() != "") sb.Append(string.Format("firstname={0}", Web.UrlEncode(Firstname)));

            if ((Lastname == null || Lastname == "") && (Name == null || Name == ""))
                throw new InvalidOperationException("Note that only one of the parameters lastname or name is required. However, we recommend providing as much information as you have as that will give better results and faster response.");

            if (Lastname == null) Lastname = "";
            sb.Append(string.Format("lastname={0};", Web.UrlEncode(Lastname))); //required by the API
            if (Name != null && Name.Trim() != "") sb.Append(string.Format("name={0};", Web.UrlEncode(Name))); //required by the API
            
            if (House != null && House.Trim() != "") sb.Append(string.Format("house={0};", Web.UrlEncode(House)));
            if (Street != null && Street.Trim() != "") sb.Append(string.Format("street={0};", Web.UrlEncode(Street)));
            if (City != null && City.Trim() != "") sb.Append(string.Format("city={0};", Web.UrlEncode(City)));
            if (State != null && State.Trim() != "") sb.Append(string.Format("state={0};", Web.UrlEncode(State)));
            if (Zip != null && Zip.Trim() != "") sb.Append(string.Format("zip={0};", Web.UrlEncode(Zip)));
            if (AreaCode != null && AreaCode.Trim() != "") sb.Append(string.Format("areacode={0};", Web.UrlEncode(AreaCode)));
            if (Metro) sb.Append(string.Format("metro={0};", (Metro ? "1" : "0")));
            string url = string.Format(URLList[QueryTypes.FindPerson], sb.ToString(), APIKey);
            return CallWhitepages(url, out Result);
        }
        public static bool FindPerson(string Name, out Whitepages.wp Result)
        {
            return FindPerson(null, null, Name, null, null, null, null, null, null, false, out Result);
        }
        public static bool FindPerson(string Name, string State, out Whitepages.wp Result)
        {
            return FindPerson(null, null, Name, null, null, null, State, null, null, false, out Result);
        }
        public static bool FindPerson(string Name, string City, string State, out Whitepages.wp Result)
        {
            return FindPerson(null, null, Name, null, null, City, State, null, null, false, out Result);
        }
        

        /// <summary>
        /// Finds information based on an address.
        /// Remarks
        ///At least one of the state, zip, or areacode parameters must be specified.
        ///
        ///Note that only the street parameter and one of the state, zip, or areacode parameters is required. However, we recommend providing as much information as you have as that will give better results and faster response.
        ///
        ///Note that the usual address line spans two parameters. The house number is separated from the street name. For a typical address such as 2468 East Main St, this would make the house parameter 2468 and the street parameter "East Main St". Of course, spaces in the street name should be encoded as %20 for passing over HTTP, so the street parameter as passed to the API would be "East%20Main%20St"
        ///
        ///Limitations
        ///A reverse_address search will return at most 10 listings using the WPAPI. Additional listings may be available by following one of the URLs returned as links with a successful search.
        /// </summary>
        /// <param name="House"></param>
        /// <param name="Apt"></param>
        /// <param name="Street"></param>
        /// <param name="City"></param>
        /// <param name="State"></param>
        /// <param name="Zip"></param>
        /// <param name="AreaCode"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        public static bool ReverseAddress(string House, string Apt, string Street, string City, string State, string Zip, string AreaCode, out Whitepages.wp Result)
        {
            if ((State == null || State == "") &&
                (Zip == null || Zip == "") &&
                (AreaCode == null || AreaCode == "")
                )
                throw new InvalidOperationException("At least one of the state, zip, or areacode parameters must be specified.");


            System.Text.StringBuilder sb = new StringBuilder();
            if (House != null && House.Trim() != "") sb.Append(string.Format("house={0};", Web.UrlEncode(House)));
            if (Street != null && Street.Trim() != "") sb.Append(string.Format("street={0};", Web.UrlEncode(Street)));
            if (City != null && City.Trim() != "") sb.Append(string.Format("city={0};", Web.UrlEncode(City)));
            if (State != null && State.Trim() != "") sb.Append(string.Format("state={0};", Web.UrlEncode(State)));
            if (Zip != null && Zip.Trim() != "") sb.Append(string.Format("zip={0};", Web.UrlEncode(Zip)));
            if (AreaCode != null && AreaCode.Trim() != "") sb.Append(string.Format("areacode={0};", Web.UrlEncode(AreaCode)));
            if (Apt != null && Apt.Trim() != "") sb.Append(string.Format("apt={0};", Web.UrlEncode(Apt)));
            string url = string.Format(URLList[QueryTypes.ReverseAddress], sb.ToString(), APIKey);
            return CallWhitepages(url, out Result);
        }
        public static bool ReverseAddress(string House, string Street, string City, string State, out Whitepages.wp Result)
        {
            return ReverseAddress(House, null, Street, City, State, null, null, out Result);
        }
        public static bool ReverseAddress(string Zip, string AreaCode, out Whitepages.wp Result)
        {
            return ReverseAddress(null, null, null, null, null, Zip, null, out Result);
        }

        /// <summary>
        /// Finds contact information for a business, based on location and either name or category.
        /// Notes
        ///Note that either the businessname or the category parameter is required. In most cases, we recommend providing as much information as you have since that will give better results and faster response.  However, only one of the parameters businessname or category should be supplied, as searches with both of these parameters will generally not produce results.
        ///
        ///Note that the usual address line spans two parameters. The house number is separated from the street name. For a typical address such as 2468 East Main St, this would make the house parameter 2468 and the street parameter "East Main St". Of course, spaces in the street name should be encoded as %20 for passing over HTTP, so the street parameter as passed to the API would be "East%20Main%20St"
        ///
        ///The metro parameter is a Boolean value where 1 signifies true or 0 signifies false. It specifies whether to expand the search to include the metropolitan area around the specified city parameter value.
        ///
        ///If a find_business search using a categroy parameter is ambiguous, the return will be a picklist of possible categories.
        ///
        ///Limitations
        ///A find_business search will return at most 20 listings using the WPAPI. Additional listings may be available by following one of the URLs returned as links with a successful search.
        ///
        /// </summary>
        /// <param name="Firstname"></param>
        /// <param name="Lastname"></param>
        /// <param name="Name"></param>
        /// <param name="House"></param>
        /// <param name="Street"></param>
        /// <param name="City"></param>
        /// <param name="State"></param>
        /// <param name="Zip"></param>
        /// <param name="AreaCode"></param>
        /// <param name="Metro"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        public static bool FindBusiness(string BusinessName, string Category, string House, string Street, string City, string State, string Zip, string AreaCode, bool Metro, out Whitepages.wp Result)
        {
            System.Text.StringBuilder sb = new StringBuilder();

            if ((BusinessName == null || BusinessName == "") && (Category == null || Category == ""))
                throw new InvalidOperationException("Note that either the businessname or the category parameter is required. In most cases, we recommend providing as much information as you have since that will give better results and faster response.");

            if (BusinessName != null && BusinessName.Trim() != "") sb.Append(string.Format("businessname={0};", Web.UrlEncode(BusinessName)));
            if (Category != null && Category.Trim() != "") sb.Append(string.Format("category={0};", Web.UrlEncode(Category)));
            if (House != null && House.Trim() != "") sb.Append(string.Format("house={0};", Web.UrlEncode(House)));
            if (Street != null && Street.Trim() != "") sb.Append(string.Format("street={0};", Web.UrlEncode(Street)));
            if (City != null && City.Trim() != "") sb.Append(string.Format("city={0};", Web.UrlEncode(City)));
            if (State != null && State.Trim() != "") sb.Append(string.Format("state={0};", Web.UrlEncode(State)));
            if (Zip != null && Zip.Trim() != "") sb.Append(string.Format("zip={0};", Web.UrlEncode(Zip)));
            if (AreaCode != null && AreaCode.Trim() != "") sb.Append(string.Format("areacode={0};", Web.UrlEncode(AreaCode)));
            if(Metro) sb.Append(string.Format("metro={0};", (Metro ? "1" : "0")));
            string url = string.Format(URLList[QueryTypes.FindBusiness], sb.ToString(), APIKey);
            return CallWhitepages(url, out Result);
        }
        public static bool FindBusiness(string BusinessName, out Whitepages.wp Result)
        {
            return FindBusiness(BusinessName, null, null, null, null, null, null, null, false, out Result);
        }
        public static bool FindBusiness(string BusinessName, string City, string State, out Whitepages.wp Result)
        {
            return FindBusiness(BusinessName, null, null, null, City, State, null, null, false, out Result);
        }
        public static bool FindBusiness(string BusinessName, string City, string State, bool Metro, out Whitepages.wp Result)
        {
            return FindBusiness(BusinessName, null, null, null, City, State, null, null, Metro, out Result);
        }


        /// <summary>
        /// Finds a name and contact information, based on a telephone number.
        /// Remarks
        ///The phone parameter is required and the state parameter is also required if the phone parameter value is 7 digits.
        ///
        ///Limitations
        ///A reverse_phone search will return at most 5 listings using the WhietPages.com API. Additional listings may be available by following one of the URLs returned as links with a successful search.
        ///
        /// </summary>
        /// <param name="Phone"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        public static bool ReversePhone(string Phone, out Whitepages.wp Result)
        {
            string url = string.Format(URLList[QueryTypes.ReversePhone], Phone, APIKey);
            return CallWhitepages(url, out Result);
        }

        private static bool CallWhitepages(string URL, out Whitepages.wp Result)
        {
            if (APIKey == null) throw new InvalidOperationException("You must specify a valid API Key");
            string xml = Web.HTTPAsString(URL);
            Result = (Whitepages.Serialize.DeSerializeXML(xml, typeof(Whitepages.wp), true) as Whitepages.wp);
            System.Threading.Thread.Sleep(500);
            return (Result.result.type == responsetype.success);
        }
    }
}
